November 14, 2018

htmlWidgets

Very smart people have been working on creating interactive graphics in R for a long time. So far, nothing “native” to" R has taken off in a big way (although keep an eye out for ggvis).

There is now a series of R packages that allow you to create plots from these JavaScript libraries from within R.

There is a website with much more on these htmlWidgets at http://www.htmlwidgets.org.

htmlWidgets

JavaScript has developed a number of interactive graphics libraries that can be for documents viewed in a web browser. These work by binding data to support vector graphics (SVGs).

They allow you to do things like zoom and pan. The graphics can also “react” to certain events. For example, they can show a pop-up when you hover over or click on a point.

htmlWidgets

Some of the packages availabe to help you create interactive graphics from R using JavaScript graphics libraries:

  • leaflet: Mapping
  • dygraphs: Time series
  • plotly: A variety of plots, including maps
  • rbokeh: A variety of plots, including maps
  • networkD3: Network data
  • d3heatmap: Heatmaps
  • DT: Data tables
  • DiagrammeR: Diagrams and flowcharts

htmlWidgets

These packages can be used to make some pretty cool interactive visualizations for HTML output from R Markdown or Shiny (you can also render any of theme in RStudio).

There are, however, a few limitations:

  • Written by different people. The different packages have different styles as well as different interfaces. Learning how to use one package may not help you much with other of these packages.
  • Many are still in development, often in early development.

Example data

library(tigris)
denver_tracts <- tracts(state = "CO", county = 31, 
                        cb = TRUE, class = "sf")
load("../data/fars_colorado.RData")
accident_data <- driver_data %>%
  dplyr::select(state, st_case, county, latitude, longitud,
                date, fatals, drunk_dr) %>%
  dplyr::filter(county == 31 & longitud < -104.1) %>%
  dplyr::distinct()

Leaflet

“Leaflet” is a JavaScript library for making interactive maps. You can find out more about the JavaScript version here: http://leafletjs.com

The leaflet package brings this functionality to R. The R Studio group has created a website on leaflet: http://rstudio.github.io/leaflet/. This website walks you through different options available with leaflet.

library(leaflet)

Add map background

If you just run leaflet(), you just get a blank leaflet area:

leaflet()

Add map background

In leaflet, the map background is composed of tiles. To get something more interesting, you’ll need to add tiles to your leaflet map. If you don’t include any other data, the leaflet map will include the world:

leaflet() %>%
  addTiles()

Add map background

Adding markers

For htmlWidgets, points are often referred to as markers.

Once you add these markers, the map will automatically scale to a reasonable size for their bounding box.

leaflet() %>%
  addTiles() %>%
  addMarkers(data = accident_data, lng = ~ longitud, lat = ~ latitude)

Use lng and lat to tell R which columns contain data on longitude and latitude for each point. This is not needed if you are using a spatial object (e.g., SpatialPointsDataFrame). Further, R will try to guess the columns in a regular dataframe.

Adding markers

Adding markers

You can use several types of R objects for your data for leaflet:

  • Dataframe with columns for latitude and longitude
  • Simple feature objects
  • Latitude-longitude matrix

Adding markers

You can choose circles for your markers instead by using addCircleMarkers. You can adjust the circle size with radius.

leaflet() %>%
  addTiles() %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude)

The radius argument specifies the size of the circle. For CircleMarkers, the size will reset as you zoom in and out. If you want something with a constant radius (e.g., in meters), you can add Circles.

Adding markers

Adding markers

If you have a lot of overlapping data, you can also use the clusterOptions argument to show the markers as clusters that group together when you zoom out but split up when you zoom in:

leaflet() %>%
  addTiles() %>%
  addMarkers(data = accident_data, 
                   lng = ~ longitud, lat = ~ latitude,
                   clusterOptions = markerClusterOptions())

Adding markers

Add map background

For the background, the default is to use map tiles from OpenStreetMap. However, you can change the source of the tiles by using addProviderTiles. For example, to use Stamen Watercolor, you can call:

leaflet() %>%
  addProviderTiles("Stamen.Watercolor") %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude)

Add map background

Add map background

leaflet() %>%
  addProviderTiles("Esri.WorldStreetMap") %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude)

Add map background

Pop-ups

You can use the popup option to show information when the user clicks on a marker.

It’s easiest to do this if you have the information you want to show in the dataframe with the location data. For example, we have date-time, number of fatalities, and number of drunk drivers in this data:

accident_data %>% 
  dplyr::select(date, fatals, drunk_dr) %>% 
  dplyr::slice(1:3)
##                  date fatals drunk_dr
## 1 2001-01-04 19:00:00      1        1
## 2 2001-01-03 07:00:00      1        1
## 3 2001-01-05 20:00:00      1        1

Pop-ups

If we want to show day of the week, month, hour, and number of fatalities, go ahead and calculate any value not already in the dataset:

library(lubridate)
accident_data <- accident_data %>%
  mutate(weekday = wday(date, label = TRUE, abbr = FALSE),
         month = month(date, label = TRUE, abbr = FALSE),
         hour = format(date, format = "%H:%M"))

Pop-ups

The popup text needs to be a character vector, written in HTML syntax. You can create that vector first, and then pass it to the popup argument.

popup_info <- paste0("<b>Weekday:</b>  ", 
                                  accident_data$weekday, "<br/>",
                                  "<b>Month:</b>  ",
                                  accident_data$month, "<br/>",
                                  "<b>Hour:</b>  ",
                                  accident_data$hour, "<br/>",
                                  "<b>Fatalities:</b>  ",
                                  accident_data$fatals)
popup_info[1:3]
## [1] "<b>Weekday:</b>  Thursday<br/><b>Month:</b>  January<br/><b>Hour:</b>  19:00<br/><b>Fatalities:</b>  1" 
## [2] "<b>Weekday:</b>  Wednesday<br/><b>Month:</b>  January<br/><b>Hour:</b>  07:00<br/><b>Fatalities:</b>  1"
## [3] "<b>Weekday:</b>  Friday<br/><b>Month:</b>  January<br/><b>Hour:</b>  20:00<br/><b>Fatalities:</b>  1"

Pop-ups

Now pass that vector to the popup argument for the layer you want to pair it with:

leaflet() %>%
  addTiles() %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude,
                   popup = popup_info)

Pop-ups

Pop-ups

Mapping values to color

To use color to show a value, you need to do a few things. First, you need to the the colorFactor function (or another in its family) to create a function for mapping from values to colors. Then, you need to use this within the call to add the markers.

library(viridisLite)
pal <- colorFactor(viridis(5), accident_data$drunk_dr)
leaflet() %>%
  addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude,
                   popup = popup_info, 
                   color = pal(accident_data$drunk_dr)) 

Mapping values to color

Mapping values to color

The colorFactor function (and friends) are a pretty cool type of function that actually creates a new function:

pal <- colorFactor(viridis(5), accident_data$drunk_dr)
class(pal)
## [1] "function"
head(pal)
##                                                  
## 1 structure(function (x)                         
## 2 {                                              
## 3     if (length(x) == 0 || all(is.na(x))) {     
## 4         return(rep.int(na.color, length(x)))   
## 5     }                                          
## 6     lvls <- getLevels(domain, x, lvls, ordered)

Adding a legend

Once you are showing something with color, you can add a legend to explain it. You can do that with the addLegend function, which must include values for the color palette and values for each point from this color palette.

library(viridisLite)
pal <- colorFactor(viridis(5), accident_data$drunk_dr)
leaflet() %>%
  addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude,
                   popup = popup_info, 
                   color = pal(accident_data$drunk_dr)) %>%
  addLegend(pal = pal, values = accident_data$drunk_dr)

Adding a legend

Adding polygons

You can add polygons with the addPolygons function.

leaflet() %>%
  addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
  addPolygons(data = denver_tracts)

Adding polygons

Adding polygons

You can add popups for polygons, as well.

polygon_popup <- paste0("Tract ID:  ", 
                        denver_tracts$TRACTCE)
leaflet() %>%
  addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
  addPolygons(data = denver_tracts, popup = polygon_popup)

Adding polygons

Adding polygons

You can overlay different elements. For example, you can show both accidents and tracts:

polygon_popup <- paste0("Tract ID:  ", 
                        denver_tracts$TRACTCE)
leaflet() %>%
  addProviderTiles("OpenStreetMap") %>%
  addPolygons(data = denver_tracts, popup = polygon_popup,
              color = "#000000", fillColor = "969696", 
              weight = 2) %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude,
                   popup = popup_info, opacity = 0.9,
                   color = pal(accident_data$drunk_dr)) %>%
  addLegend(pal = pal, values = accident_data$drunk_dr, opacity = 0.9)

Adding polygons

Adding polygons

You can add the ability for the user to pick which layers to see using addLayersControls.

leaflet() %>%
  addProviderTiles("OpenStreetMap") %>%
  addPolygons(data = denver_tracts, popup = polygon_popup,
              color = "#000000", fillColor = "969696", 
              weight = 2, group = "tracts") %>%
  addCircleMarkers(data = accident_data, radius = 2,
                   lng = ~ longitud, lat = ~ latitude,
                   popup = popup_info, opacity = 0.9,
                   color = pal(accident_data$drunk_dr), 
                   group = "accidents") %>%
  addLegend(pal = pal, values = accident_data$drunk_dr, opacity = 0.9) %>%
  addLayersControl(baseGroups = c("base map"), 
                   overlayGroups = c("tracts", "accidents"))

Adding polygons

Find out more

plotly package

From the package documentation:

“Easily translate ggplot2 graphs to an interactive web-based version and / or create custom web-based visualizations directly from R.”

  • Like many of the packages today, draws on functionality external to R, but within a package that allows you to work exclusively within R.
  • Allows you to create interactive graphs from R. The functions extend much of the ggplot2 code you’ve learned.
  • Interactivity will only work within RStudio or on documents rendered to HTML.

plotly package

The plotly package allows an interface to let you work with plotly.js code directly using R code.

plotly.js is an open source library for creating interactive graphs in JavaScript. This JavaScript library is built on d3.js (Data-Driven Documents), which is a key driver in interactive web-based data graphics today.

plotly package

There are two main ways of create plots within plotly:

  • Use one of the functions to create a customized interactive graphic:
    • plot_ly: Workhorse of plotly, renders most non-map types of graphs
    • plot_geo, plot_mapbax: Specific functions for creating plotly maps.
  • Create a ggplot object and then convert it to a plotly object using the ggplotly function.

plotly package

library(faraway); data(worldcup); library(dplyr)
library(plotly)
a <- worldcup %>% ggplot(aes(x = Time, y = Shots)) + geom_point()
ggplotly(a)

plotly package

a <- worldcup %>% ggplot(aes(x = Time, y = Shots, color = Position)) + 
  geom_point()
ggplotly(a)

plotly package

You can also use this with other ggplot2 functionality, like faceting:

shots_vs_time <- worldcup %>%
  mutate(Name = rownames(worldcup)) %>%
  filter(Team %in% c("Netherlands", "Germany", "Spain", "Uruguay")) %>%
  ggplot(aes(x = Time, y = Shots, color = Position, group = Name)) + 
  geom_point() + 
  facet_wrap(~ Team)
ggplotly(shots_vs_time)

plotly package

plotly package

If you pipe to the rangeslider function, it allows the viewer to zoom in on part of the x range. (This can be particularly nice for time series.)

You should have a dataset available through your R session named USAccDeaths. This gives a monthly county of accidental deaths in the US for 1973 to 1978. This code will plot it and add a range slider on the lower x-axis.

plot_ly(x = time(USAccDeaths), y = USAccDeaths) %>% 
  add_lines() %>% rangeslider()

plotly package

plotly package

For a 3-D scatterplot, add a mapping to the z variable:

worldcup %>%
  plot_ly(x = ~ Time, y = ~ Shots, z = ~ Passes,
          color = ~ Position, size = I(4)) %>%
  add_markers()

plotly package

plotly package

The volcano data comes with R and is in a matrix format. Each value gives the elevation for a particular pair of x- and y-coordinates.

dim(volcano)
## [1] 87 61
volcano[1:4, 1:4]
##      [,1] [,2] [,3] [,4]
## [1,]  100  100  101  101
## [2,]  101  101  102  102
## [3,]  102  102  103  103
## [4,]  103  103  104  104

plotly package

plot_ly(z = ~ volcano, type = "surface")

plotly package

Mapping with plotly can build on some data that comes with base R or other packages you’ve likely added (or can add easily, as with the map_data function from ggplot2). For example, we can map state capitals and cities with > 40,000 people using data in the us.cities dataframe in the maps package:

head(maps::us.cities, 3)
##         name country.etc    pop   lat    long capital
## 1 Abilene TX          TX 113888 32.45  -99.74       0
## 2   Akron OH          OH 206634 41.08  -81.52       0
## 3 Alameda CA          CA  70069 37.77 -122.26       0

plotly package

Here is code you can use to map all of these cities on a US map:

ggplot2::map_data("state") %>%
   group_by(group) %>% 
   plot_ly(x = ~long, y = ~lat) %>%
   add_polygons(hoverinfo = "none") %>%
   add_markers(text = ~paste(name, "<br />", pop), hoverinfo = "text",
               alpha = 0.25,
     data = filter(maps::us.cities, -125 < long & long < -60 &
                     25 < lat & lat < 52)) %>%
   layout(showlegend = FALSE)

plotly package

plotly package

The creator of the R plotly package has written a bookdown book on the package that you can read here. It provides extensive details and examples for using plotly.

rbokeh package

rbokeh package

library(rbokeh)
figure(width = 600, height = 300) %>%
  ly_points(Time, Shots, data = worldcup,
    color = Position, hover = list(Time, Shots))

rbokeh package

dygraphs package

The dygraphs package lets you create interactive time series plots from R using the dygraphs JavaScript library.

The main function syntax is fairly straightforward. Like many of these packages, it allows piping.

There is a website with more information on using dygraphs available at http://rstudio.github.io/dygraphs/index.html.

dygraphs package

For example, here is the code to plot monthly deaths from lung diseases in the UK in the 1970s.

library(dygraphs)
lungDeaths <- cbind(mdeaths, fdeaths)
dygraph(lungDeaths) %>%
  dySeries("mdeaths", label = "Male") %>%
  dySeries("fdeaths", label = "Female")

dygraphs package

For example, here is the code to plot monthly deaths from lung diseases in the UK in the 1970s.

DT package

The DT package provides a way to create interactive tables in R using the JavaScript DataTables library.

We’ve already seen some examples of this output in some of the Shiny apps I showed last week. You can also use this package to include interactive tables in R Markdown documents you plan to render to HTML.

There is a website with more information on this package at http://rstudio.github.io/DT/.

DT package

library(DT)
datatable(worldcup)

networkD3 package

The networkd3 package allows you to create different networks. For example, a simple network:

library(networkD3)
src <- c("A", "A", "A", "A",
        "B", "B", "C", "C", "D")
target <- c("B", "C", "D", "J",
            "E", "F", "G", "H", "I")
networkData <- data.frame(src, target)
simpleNetwork(networkData)

networkD3 package

networkD3 package

A more complex network:

data(MisLinks)
data(MisNodes)
forceNetwork(Links = MisLinks, Nodes = MisNodes,
            Source = "source", Target = "target",
            Value = "value", NodeID = "name",
            Group = "group", opacity = 0.8)

networkD3 package

networkD3 package

A Sankey diagram:

URL <- paste0(
        "https://cdn.rawgit.com/christophergandrud/networkD3/",
        "master/JSONdata/energy.json")
Energy <- jsonlite::fromJSON(URL)
sankeyNetwork(Links = Energy$links, Nodes = Energy$nodes, 
              Source = "source", Target = "target", 
              Value = "value", NodeID = "name",
              units = "TWh", fontSize = 12, 
              nodeWidth = 30)

networkD3 package

Creating your own widget

If you find a JavaScript visualization library and would like to create bindings to R, you can create your own package for a new htmlWidget.

There is advice on creating your own widget for R available at http://www.htmlwidgets.org/develop_intro.html.